﻿/**
 * Licensed under the MIT License
 *
 * Copyright (c) 2010 Alexandre Croiseaux
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
package marcel.core
{
	import marcel.debug.*;
	import marcel.events.FileLoaderEvent;
	import marcel.external.FileLoader;
	import marcel.external.FileType;
	import marcel.external.Stats;
	import marcel.utils.StringUtils;

	import com.asual.swfaddress.SWFAddress;
	import com.asual.swfaddress.SWFAddressEvent;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.URLVariables;
	import flash.system.Capabilities;
	import flash.utils.Dictionary;
	
	
	/**
	* Dispatched when the config XML site is loaded and completely parsed.
	* @eventType flash.events.Event.COMPLETE
	*/
	[Event(name="complete", type="flash.events.Event")]
	
	/**
	 * Class used to create site configuration from /xml/config/site.xml.
	 * The class parse FlashVars, loads the site xml and stores application vars.
	 * @author Alexandre Croiseaux
	 */
	public class Config extends EventDispatcher
	{
		//--------------------------------------------------------------------------
		//
		//  Public vars
		//
		//--------------------------------------------------------------------------
		/**
		 * Defines the path of the xml file to load and parse for config building
		 * This variable could be is overridden with a flashvar named 'site_xml'
		 */
		public var siteXML:String = "../xml/config/site.xml";
		/**
		 * Defines the lang param found in config site xml or in flashvars
		 */
		public var lang:String;
		/**
		 * Defines the coutnry param found in config site xml or in flashvars
		 */
		public var country:String;
		
		//--------------------------------------------------------------------------
		//
		//  Private vars
		//
		//--------------------------------------------------------------------------
		private var _xml:XML;
		private var _xmlsUIs:XMLList;
		private var _xmlsAssets:XMLList;
		private var _xmlsStates:XMLList;
		private var _vars:Dictionary;
		private var _xmlLoader:FileLoader;
		private var _built:Boolean = false;
		private var _plugin:Plugin;
		private var _swfAddress:Object;
		private var _swfAddressReverse:Object;
		
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		public function Config(plugin:Plugin)
		{
			_plugin = plugin;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Public methods
		//
		//--------------------------------------------------------------------------
		/**
		 * Loads the site xml at "../xml/config/site.xml", or at the URI specified by a 'site_xml' FlashVar, parses the xml and then dispatchs an Event.COMPLETE event.
		 */
		public function build():void
		{
			if (_plugin.flashVars.isSet("site_xml")) siteXML = _plugin.flashVars.getValue("site_xml");
			
			debugi("Building Project Configuration from '" + siteXML + "' (flash player " + Capabilities.playerType + " " + Capabilities.version + (Capabilities.isDebugger ? " DEBUG" : "") + ")");
			
			SWFAddress.addEventListener(SWFAddressEvent.INIT, onSWFAddressInit);
		}
		
		/**
		 * Indicates if the config is built, ie if the 'build' method has been called.
		 * @return	true if the config is built, false otherwise.
		 */
		public function isBuilt():Boolean { return _built; }
		
		/**
		 * Returns a variable specified in the site config xml file; the variable value is automatically parsed using the parseValue method.
		 * Each 'var' node value in the site config xml file can be overriden by a FlashVar.
		 * @param	name	The variable name to get the value of
		 * @return	The variable value as a string
		 */
		public function getVar(name:String):String
		{
			if (!hasVar(name)) debuge("[Config] Variable '" + name + "' not defined");
			return parseValue(_vars[name]);
		}
		
		/**
		 * Overrides a config variable
		 * @param	name	The variable name to override
		 * @param	value	Variable value
		 */
		public function setVar(name:String, value:String):void { _vars[name] = value; }
		
		/**
		 * Indicates if a config variable with the specified name exists
		 * @param	name	The variable name to override
		 * @return	true if the variable is set, false otherwise
		 */
		public function hasVar(name:String):Boolean { return _vars[name] != undefined; }
		
		/**
		 * Logs all configuration variables to the availables loggers
		 */
		public function debugVars():void
		{
			for (var key:String in _vars) debugi(key + " = " + parseValue(_vars[key]));
		}
		
		/**
		 * Returns the default state name.
		 * @return	the default state name.
		 */
		public function getDefaultState():String
		{
			var s1:String = getVar("state");
			var s2:String = getStateFromSWFAddress(s1);
			if (hasState(s2)) return s2;
			if (hasState(s1)) return s1;
			
			// not found, look for value in xml file
			if (hasVar("state_xml"))
			{
				var s3:String = getVar("state__xml");
				if (hasState(s3)) return s3;
			}
			
			return s1;
		}
		
		/**
		 * Parses a configuration variable recursively.
		 * Each configuration variable can contains one or several other configuration variables values defined above.
		 * <pre>
		 * Usage :
		 * &lt;var name="myvar"&gt;myvalue&lt;/var&gt;
		 * &lt;var name="myvar_extended"&gt;{myvar}_extended&lt;/var&gt;
		 * </pre>
		 * @param	value	string value to parse
		 * @return	The variable value as a string
		 */
		public function parseValue(value:String):String
		{
			if (value == null) return value;
			
			var pattern:RegExp = /{(.*?)}/g;
			var result:Object = pattern.exec(value);
			while (result != null)
			{
				var index:String = result.index;
				var fullKey:String = result[0];
				var key:String = result[1];
				var val:String = _vars[key];
				if (_vars[key] == undefined)
				{
					if (_plugin.assetManager.hasAsset(key))
					{
						var xml:* = _plugin.assetManager.getXMLAsset(key);
						var a:Array = value.split(".");
						a.shift();
						while (a.length > 0) xml = xml[a.shift()];
						return value.split("{" + key + "}").shift() + xml.toString();
					}
					else
					{
						// stop infinite loop
						return value;
					}
				}
				else value = value.replace(fullKey, val);
				result = pattern.exec(value);
			}
			
			if (pattern.test(value)) return parseValue(value);
			return value;
		}
		
		/**l
		 * Indicates if the config file contains an UI with the specified id.
		 * @param	id	the UI id to find, using the node 'id' attribute.
		 * @return	true if the config file contains the UI, false otherwise.
		 */
		public function hasUI(id:String):Boolean
		{
			return _xmlsUIs.(@id == id) != undefined;
		}
		
		/**
		 * Indicates if the config file contains an state with the specified id.
		 * @param	id	the state id to find, using the node 'id' attribute.
		 * @return	true if the config file contains the state, false otherwise.
		 */
		public function hasState(id:String):Boolean
		{
			return _xmlsStates.(@id == id) != undefined;
		}
		
		/**
		 * Return the URL of the specified asset (with versioning).
		 * @param	id	the asset id to get the URL from
		 * @return	the string URL.
		 */
		public function getAssetUri(assetId:String):String
		{
			var assetXML:XML = getAssetNode(assetId);
			var useMagick:Boolean = parseValue(assetXML.@usemagic.toString()) == "true";
			var versionValue:Number = Number(parseValue(assetXML.@version.toString()));
			var version:String = "";
			
			var uri:String = parseValue(assetXML.text().toString());
			if (uri.length == 0) return "";
			
			var versionPrefix:String = StringUtils.contains(uri, '?') ? "?&=" : "?v=";
			if (!isNaN(versionValue) && versionValue > 0) version = versionPrefix + (versionValue + getAssetsVersion());
			if (useMagick) version = "?v=" + (new Date()).getTime();
			
			var fullUri:String = Plugin.plugin.isLocalServed() ? uri : uri + version;
			return fullUri;
		}
		
		/**
		 * Returns the site config xml
		 * @return An XML instance
		 */
		public function getConfigXML():XML
		{
			return _xml;
		}
		
		/**
		 * Returns a list of all the UIs IDs specified in the config xml file
		 */
		public function getFullUIsList():/*String*/Array
		{
			var a:Array = [];
			for each (var node:XML in _xmlsUIs)
			{
				a.push(parseValue(node.@id.toString()));
			}
			return a;
		}
		
		/**
		 * Sends a stat tag.
		 * @param	statIdOrTag	A string, the identifier of the tag as defined in the xml configuration file, or the tag itself
		 * @param	params		An array of parameters, considered only if statIdOrTag is a tag, not an identifier
		 * @see Stats.addStat
		 * @see Stats.enableTypes
		 */
		public function sendStat(statIdOrTag : String, params : Array = null):void
		{
			if (!statIdOrTag) return;
			
			var node:XMLList = _xml.stats.stat.(@id == statIdOrTag);
			if (node.@tag.toString())
			{
				statIdOrTag = parseValue(node.@tag.toString());
				if (node.@params.toString()) params = StringUtils.trimSplit(parseValue(node.@params.toString()));
			}
			
			var _aRestrictTypes:Array = [];
			if (node.@types.toString())
			{
				_aRestrictTypes = StringUtils.trimSplit(parseValue(node.@types.toString()));
				debug('Restrict types tag');
				debug(_aRestrictTypes);
			}
			
			Stats.addStat(statIdOrTag, params, _aRestrictTypes);
		}
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Internal methods
		//
		//--------------------------------------------------------------------------
		/**
		 * @private
		 * Returns the global assets version specified in the 'assets' node of the xml config file.
		 * @return	assets global version as Number.
		 */
		internal function getAssetsVersion():Number
		{
			return Number(_xml.assets.@version);
		}
		
		/**
		 * @private
		 * Returns a list of all 'asset' xml nodes required by an state
		 * @param	stateId	The id to find, using the node 'id' attribute.
		 * @return	an XMLList instance, containg config 'asset' nodes.
		 */
		internal function getStateAssetsNodes(stateId:String, excludeSharedAssets:Boolean = false, includeLoadOnStartAssets:Boolean = true):XMLList
		{
			if (stateId == null) return new XMLList();
			
			// get required uis
			var uis:Array = getStateUIsToOpen(stateId);
			
			// get all assets nodes
			var list:XMLList = new XMLList();
			if (includeLoadOnStartAssets) {
				for each (var node:XML in _xmlsAssets.(attribute("load_on_start") == "true")) { 
					if (node.@cond != undefined) {
						if (validateCondition(node.@cond))  list += node;
					} else {
						list += node;
					}
				}
			}
			for (var i:uint = 0; i < uis.length; i++)
			{
				var assets:XMLList = getUIAssetsNodes(uis[i], excludeSharedAssets);
				for each (node in assets)
				{
					if (list.(@id == node.@id).length() == 0) {
						if (node.@cond != undefined) {
							if (validateCondition(node.@cond))  list += node;
						} else {
							list += node;
						}
					}
				}
			}
			
			return list;
		}
		
		/**
		 * @private
		 * Returns a list of all 'asset' xml nodes required by an UI
		 * @param	id	The UI id to find, using the node 'id' attribute.
		 * @return	an XMLList instance, containg config 'asset' nodes.
		 */
		internal function getUIAssetsNodes(id:String, excludeSharedAssets:Boolean = false):XMLList
		{
			var i:uint;
			
			// check UI
			var node:XML = getUINode(id);
			if (node.@id != id)
			{
				debugw("UI '" + id + "' not found in site.xml!");
				return new XMLList();
			}
			
			// get assets ids
			var uiAssetsNames:Array = getUIAssetsNames(id);
			
			// if exclude, remove opened uis assets names
			if (excludeSharedAssets)
			{
				var uis:/*UI*/Array = _plugin.stateManager.getOpenedUIs();
				var nextStateUisNames:/*String*/Array = getStateUIsToOpen(_plugin.stateManager.getNextState(), false); // we do NOT remove next state assets
				var openedUIsAssets:/*String*/Array = [];
				for (i = 0; i < uis.length; i++) openedUIsAssets = openedUIsAssets.concat(getUIAssetsNames(uis[i].name));
				for (i = 0; i < nextStateUisNames.length; i++) openedUIsAssets = openedUIsAssets.concat(getUIAssetsNames(nextStateUisNames[i]));
				for (i = 0; i < openedUIsAssets.length; i++)
				{
					var index:int = uiAssetsNames.indexOf(openedUIsAssets[i]);
					if (index > -1) uiAssetsNames.splice(index, 1);
				}
			}
			
			// create list of nodes
			var list:XMLList = new XMLList();
			for (i = 0; i < uiAssetsNames.length; i++)
			{
				var assetName:String = uiAssetsNames[i];
				node = getAssetNode(assetName);
				if (node.toString().length == 0) debugw("Asset '" + assetName + "' needed by UI '" + id + "' not found in site.xml!");
				else if (excludeSharedAssets && node.attribute("load_on_start") == "true") continue;
				else list[i] = node;
			}
			
			return list;
		}
		
		/**
		 * @private
		 * Returns the list of UIs ids to open for the specific state
		 * @param	stateId		The id of the state to open
		 * @param	includePermanentUIs		indicats if the permanent opend uis must be added to the returned array. If true, permanent uis are added at the begining of the returned array.
		 * @return An array containing the UIs IDs
		 */
		internal function getStateUIsToOpen(stateId:String, includePermanentUIs:Boolean = true):/*String*/Array
		{
			var a:Array = StringUtils.trimSplit(parseValue(getStateNode(stateId).@open_uis.toString()));
			if (includePermanentUIs)
			{
				a = StringUtils.trimSplit(parseValue(_xml.states.@permanent_uis.toString())).concat(a);
			}
			return a;
		}
		
		/**
		 * @private
		 * Returns the list of UIs ids to close for the specific state
		 * @return An array containing the UIs IDs
		 */
		internal function getStateUIsToClose(stateId:String):/*String*/Array
		{
			var res:Array = [];
			var all:Array = getFullUIsList();
			var keep:Array = StringUtils.trimSplit(parseValue(getStateNode(stateId).@keep_uis.toString()));
			if (keep.indexOf("all") > -1) return res;
			keep = keep.concat(getStateUIsToOpen(stateId));
			
			for (var i:int = 0; i < all.length; i++)
			{
				if (keep.indexOf(all[i]) == -1) res.push(all[i].toString());
			}
			
			return res;
		}
		
		/**
		 * @private
		 * Return the SWFAddress value to put in the address bar for the specified state
		 * @param	stateId The state ID to get the SWFAddress reference from
		 * @return	A String representing the path to put into the browser address bar
		 */
		internal function getStateSWFAddress(stateId:String):String
		{
			if (_swfAddress == null) initSWFAddress();
			return _swfAddress[stateId];
		}
		
		/**
		 * @private
		 * Return the state id from the specified SWFAddress path. This method will search a state having a swfaddress attribute beggining with the specified value
		 * Ex : if a state has a swfaddress attribute '/home', if a sepcified '/home/foo', the state with '/home' will be returned.
		 * @param	swfAddressId the swfaddress path to get the state id from
		 * @return	A String representing the state id
		 */
		internal function getStateFromSWFAddress(swfAddressId:String):String
		{
			if (_swfAddress == null) initSWFAddress();
			
			if (swfAddressId.charAt(0) != "/") swfAddressId = "/" + swfAddressId;
			for (var name:String in _swfAddressReverse)
			{
				if (StringUtils.beginsWith(swfAddressId, name)) return _swfAddressReverse[name];
			}
			return _swfAddressReverse[swfAddressId];
		}
		
		/**
		 * @private
		 * Returns the 'title' attribute specified in the config xml for the state passed as argument
		 * @param	stateId	the state id to get the title from
		 * @return	a string representing the title to put in the HTML page
		 */
		internal function getStateTitle(stateId:String):String { return parseValue(getStateNode(stateId).@title.toString()); }
		
		/**
		 * @private
		 * Returns the 'stat' attribute specified in the config xml for the state passed as argument
		 * @param	stateId	the state id to get the stat from
		 * @return	a string representing the stat data
		 */
		internal function getStateStatId(stateId:String):String { return parseValue(getStateNode(stateId).@stat.toString()); }
		
		/**
		 * @private
		 * Returns the id of the UI that must be used as the application main loader
		 * @return	A string representing the UI id
		 */
		internal function getStatesMainLoader():String { return parseValue(_xml.states.@main_loader.toString()); }
		
		/**
		 * @private
		 * Returns id of the UI that must be used as the loader for the state passed as argument
		 * @param	stateId	the state id to get the loader ui from
		 * @return	A string representing the loader UI id
		 */
		internal function getStateLoader(stateId:String):String
		{
			var str:String = parseValue(getStateNode(stateId).@loader_ui.toString());
			return str.length == 0 ? getStatesMainLoader() : str;
		}
		
		/**
		 * @private
		 * Returns an object defining the delays that must be used to open or close UIs of the specified state.
		 * @param	stateId	the state id to get the delays from
		 * @param	reverse	indicates if the opening delays (false) or cloding delays (true) must be returned
		 * @return	An Object containing UIs IDs as keys, and delays as values (Ex : {my_ui:5, my_other_ui:2})
		 */
		internal function getStateDelays(stateId:String, reverse:Boolean = false):Object
		{
			// get state delays
			var delays:Array = StringUtils.trimSplit(parseValue(getStateNode(stateId).@delays.toString()));
			
			// is not reversed (ie opening sequence), we add permanent uis delays
			if (!reverse)
			{
				delays = StringUtils.trimSplit(parseValue(_xml.states.@delays.toString())).concat(delays);
			}
			
			// calcultate max value to reverse delays
			var max:Number = delays.slice().sort(Array.NUMERIC).reverse().shift();
			if (isNaN(max)) max = 0;
			
			// get uis to open, without permanent uis if reverse (ie cloding sequence)
			var uis:Array = getStateUIsToOpen(stateId, !reverse);
			var delay:Number;
			var o:Object = { };
			var a:Array = [];
			for (var i:int = 0; i < uis.length; i++)
			{
				delay = isNaN(delays[i]) ? 0 : delays[i];
				if (reverse) delay = max - delay;
				o[uis[i]] = delay;
			}
			return o;
		}
		
		/**
		 * @private
		 * Indicates if the loading process must happen before closing the current state, or after (default), as specified in the config xml file.
		 * @param	stateId	the state id to get the load process from
		 * @return	true if the load precess must happen before closing the current state, false otherwise
		 */
		internal function mustLoadStateBeforeClose(stateId:String):Boolean { return parseValue(getStateNode(stateId).@load_before_close.toString()) == "true"; }
		
		/**
		 * @private
		 */
		internal function getUINode(id:String):XML { return new XML(_xmlsUIs.(@id == id).toXMLString()); }
		/**
		 * @private
		 */
		internal function getAssetNode(id:String):XML { return new XML(_xmlsAssets.(@id == id).toXMLString()); }
		/**
		 * @private
		 */
		internal function getStateNode(id:String):XML { return new XML(_xmlsStates.(@id == id).toXMLString()); }
		/**
		 * @private
		 */
		internal function getUIClassName(uiId:String):String { return parseValue(getUINode(uiId).@classname.toString()); }
		
		
		//--------------------------------------------------------------------------
		//
		//  Internal methods
		//
		//--------------------------------------------------------------------------
		private function getUIAssetsNames(uiId:String):/*String*/Array
		{
			var a:Array = [];
			
			var node:XML = getUINode(uiId);
			var uiAssetsNames:Array = StringUtils.trimSplit(parseValue(node.@assets.toString()));
			for (var i:uint = 0; i < uiAssetsNames.length; i++)
			{
				var assetName:String = uiAssetsNames[i];
				// handle groups
				var subAssets:Array = StringUtils.trimSplit(parseValue(getAssetNode(assetName).@group.toString()));
				if (subAssets.length > 0) a = a.concat(subAssets);
				else a.push(assetName);
			}
			
			return a;
		}
		
		private function validateCondition(cond:String):Boolean
		{
			cond = cond.split("&amp;").join("&"); // not very useful, just in case
			cond = parseValue(cond);
			
			var conds:Array;
			var parts:Array;
			var variable:String;
			var value:String;
			if (cond.indexOf("&&") != -1) {
				conds = cond.split("&&");
				for (var i:int = 0; i < conds.length; i++) {
					if (conds[i].indexOf("||") != -1) {
						if (!validateCondition(conds[i])) return false;
					} else {
						parts = conds[i].split("==");
						variable = parseValue(parts[0]);
						value = parseValue(parts[1]);
						if (variable != value) return false;
					}
				}
			} else {
				conds = cond.split("||");
				for (i = 0; i < conds.length; i++) {
					parts = conds[i].split("==");
					variable = parseValue(parts[0]);
					value = parseValue(parts[1]);
					if (variable == value) return true;
				}
				return false;
			}
			return true;
		}
		
		private function init():void
		{
			_built = true;
			
			var statTypes:String = parseValue(_xml.stats.@enable_types);
			Stats.enableTypes(statTypes ? StringUtils.trimSplit(statTypes) : []);
			_plugin.stateManager.lockViewOnChange = _xml.states.@lock_view_on_change == "true";
			
			dispatchEvent(new Event(Event.COMPLETE));
		}
		
		private function initVars():void
		{
			_vars = new Dictionary();
			for each (var element:XML in _xml.vars["var"])
			{
				var key:String = element.@name.toString();
				var val:String = element.valueOf().toString();
				
				if (element.@cond != undefined)
				{
					if (validateCondition(element.@cond)) _vars[key] = val;
				}
				else _vars[key] = val;
				
				if (_plugin.flashVars.isSet(key))
				{
					_vars[key + "__xml"] = _vars[key]; // save old value
					_vars[key] = _plugin.flashVars.getValue(key);
				}
			}
			
			lang = getVar("l");
			country = getVar("c");
			if (getVar("framerate")) _plugin.root.stage.frameRate = uint(getVar("framerate"));
		}
		
		private function initSWFAddress():void
		{
			_swfAddress = {};
			_swfAddressReverse = { };
			for each (var node:XML in _xmlsStates)
			{
				var value:String = parseValue(node.@swfaddress.toString());
				if (value.length > 0)
				{
					if (value.charAt(0) != "/") value = "/" + value;
					_swfAddress[node.@id.toString()] = value;
					_swfAddressReverse[value] = node.@id.toString();
				}
			}
		}
		
		private function loadXML():void
		{
			_xmlLoader = new FileLoader(FileType.XML);
			_xmlLoader.addEventListener(FileLoaderEvent.ERROR, onLoadXMLError);
			_xmlLoader.addEventListener(FileLoaderEvent.COMPLETE, onLoadXML);
			_xmlLoader.load(siteXML, Plugin.plugin.isStandAlonePlayer() ? null : new URLVariables("v=" + (new Date()).getTime()));
		}
		
		private function loadExtraFiles():void
		{
			for each (var node:XML in _xmlsAssets.(attribute("load_with_conf") == "true"))
			{
				var uri:String = getAssetUri(node.@id);
				var fileType:FileType = FileType[node.@type.toString().toUpperCase()];
				if (fileType == null) fileType = FileType.getTypeFromExtension(uri);
				_plugin.assetManager.enqueue(node.@id, fileType, uri);
			}
			
			if (_plugin.assetManager.getQueueLength() > 0)
			{
				_plugin.assetManager.addEventListener(FileLoaderEvent.ALL_COMPLETE, onExtraFilesLoaded);
				_plugin.assetManager.startLoad();
			}
			else init();
		}
		
		//--------------------------------------------------------------------------
		//
		//  Events
		//
		//--------------------------------------------------------------------------
		private function onLoadXMLError(evt:FileLoaderEvent):void
		{
			trace("[Config] Error loading xml : " + evt.text);
		}
		
		private function onLoadXML(evt:FileLoaderEvent):void
		{
			_xml = evt.content;
			_xmlsUIs = _xml.uis.ui;
			_xmlsAssets = _xml.assets.asset;
			_xmlsStates = _xml.states.state;
			initVars();
			loadExtraFiles();
		}
		
		private function onExtraFilesLoaded(e:FileLoaderEvent):void
		{
			_plugin.assetManager.removeEventListener(FileLoaderEvent.ALL_COMPLETE, onExtraFilesLoaded);
			init();
		}
		
		private function onSWFAddressInit(e:SWFAddressEvent):void
		{
			SWFAddress.removeEventListener(SWFAddressEvent.INIT, onSWFAddressInit);
			loadXML();
		}
	}
}
